package igor.lakienko.ru.generation.quotes.service;

import igor.lakienko.ru.generation.quotes.dto.CryptocurrencyDto;
import igor.lakienko.ru.generation.quotes.dto.MessageEventDto;
import igor.lakienko.ru.generation.quotes.entity.CryptocurrencyEntity;
import igor.lakienko.ru.generation.quotes.enums.Cryptocurrency;
import igor.lakienko.ru.generation.quotes.enums.RecommendationAnalysts;
import igor.lakienko.ru.generation.quotes.mapper.CryptocurrencyMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.ThreadLocalRandom;

/**
 * Сервис для работы с криптовалютой.
 */
@Service
@RequiredArgsConstructor
public class CryptocurrencyService {

    private final CryptocurrencyMapper cryptocurrencyMapper;

    @Value("${quote.responseSleepInMillis}")
    long responseSleepInMillis;

    @Value("${quote.quoteSizeBatch}")
    long cryptocurrencySizeBatch;

    /**
     * Постоянное получение криптовалюты с задержкой указанной в параметрах.
     */
    public MessageEventDto getCryptocurrency() {
        try {
            Thread.sleep(responseSleepInMillis);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        CryptocurrencyEntity randomCryptocurrency = getRandomCryptocurrency();
        return cryptocurrencyMapper.cryptocurrencyEntityToDto(randomCryptocurrency);
    }

    /**
     * Получения списка криптовалют в размере указанном в конфигурационном файле.
     */
    public Collection<CryptocurrencyDto> getCryptocurrencies() {
        ArrayList<CryptocurrencyEntity> cryptocurrenciesEntities = new ArrayList<>();
        for (int i = 0; i < cryptocurrencySizeBatch; i++) {
            CryptocurrencyEntity randomCryptocurrency = getRandomCryptocurrency();
            cryptocurrenciesEntities.add(randomCryptocurrency);
        }
        return cryptocurrencyMapper.cryptocurrenciesEntityToDto(cryptocurrenciesEntities);
    }

    /**
     * Получения случайной криптовалюты, со случайными параметрами.
     */
    private CryptocurrencyEntity getRandomCryptocurrency() {
        Cryptocurrency[] cryptocurrencies = Cryptocurrency.values();
        int randomCryptocurrency = ThreadLocalRandom.current().nextInt(cryptocurrencies.length);
        Cryptocurrency cryptocurrency = cryptocurrencies[randomCryptocurrency];
        float price = cryptocurrency.getPrice();
        BigDecimal bidCryptocurrency = BigDecimal.valueOf(price + ThreadLocalRandom.current()
                .nextDouble(1.0D, 10.0D)).setScale(2, RoundingMode.HALF_DOWN);
        BigDecimal offerCryptocurrency = BigDecimal.valueOf(price - ThreadLocalRandom.current()
                .nextDouble(1.0D, 10.0D)).setScale(2, RoundingMode.HALF_DOWN);
        BigInteger marketCapital = BigInteger.valueOf(ThreadLocalRandom.current()
                .nextLong(10, 1_000_000_000));
        BigInteger profit = BigInteger.valueOf(ThreadLocalRandom.current().nextLong(10, Long.MAX_VALUE));
        BigInteger cryptocurrencyVolume = BigInteger.valueOf(ThreadLocalRandom.current()
                .nextLong(10, 10_000_000));
        int ratingCryptocurrency = ThreadLocalRandom.current().nextInt(1, 15);

        float monthlyVolatility = BigDecimal.valueOf(ThreadLocalRandom.current().nextDouble(-15.0F, 15.0F))
                .setScale(2, RoundingMode.HALF_DOWN).floatValue();

        return CryptocurrencyEntity.builder()
                .cryptocurrency(cryptocurrency)
                .fullCryptocurrencyName(cryptocurrency.getDescription())
                .bid(bidCryptocurrency)
                .offer(offerCryptocurrency)
                .price(price)
                .marketCapital(marketCapital)
                .cryptocurrencyVolume(cryptocurrencyVolume)
                .totalInCirculation(profit)
                .recommendationAnalysts(getRecommendationAnalysts())
                .monthlyVolatility(monthlyVolatility)
                .ratingCryptocurrency(ratingCryptocurrency)
                .build();
    }

    private String getRecommendationAnalysts() {
        RecommendationAnalysts[] recommendationAnalysts = RecommendationAnalysts.values();
        int randomStocks = ThreadLocalRandom.current().nextInt(recommendationAnalysts.length);
        RecommendationAnalysts recommendation = recommendationAnalysts[randomStocks];
        return recommendation.getRecommendation();
    }

}
